Lernen Sie, wie Sie React Error Boundaries mit Hooks implementieren, um Fehler beim Laden von Ressourcen elegant zu behandeln und die Benutzererfahrung und Anwendungsstabilität zu verbessern.
Robustes Laden von Ressourcen in React: Fehlergrenzen mit Hooks meistern
In modernen Webanwendungen ist das asynchrone Laden von Ressourcen eine gängige Praxis. Ob es darum geht, Daten von einer API abzurufen, Bilder zu laden oder Module zu importieren – die Behandlung potenzieller Fehler beim Laden von Ressourcen ist für eine reibungslose Benutzererfahrung entscheidend. React Error Boundaries (Fehlergrenzen) bieten einen Mechanismus, um JavaScript-Fehler an jeder Stelle im Komponentenbaum ihrer untergeordneten Komponenten abzufangen, diese Fehler zu protokollieren und eine Fallback-UI anzuzeigen, anstatt die gesamte Anwendung zum Absturz zu bringen. Dieser Artikel untersucht, wie man Fehlergrenzen in Verbindung mit React Hooks effektiv zur Verwaltung von Fehlern beim Laden von Ressourcen einsetzt.
Grundlagen von Fehlergrenzen
Vor React 16 führten unbehandelte JavaScript-Fehler während des Renderings von Komponenten dazu, den internen Zustand von React zu beschädigen und bei nachfolgenden Render-Vorgängen kryptische Fehler zu verursachen. Fehlergrenzen begegnen diesem Problem, indem sie als „Catch-All“-Blöcke für Fehler fungieren, die in ihren untergeordneten Komponenten auftreten. Es handelt sich um React-Komponenten, die eine oder beide der folgenden Lebenszyklusmethoden implementieren:
static getDerivedStateFromError(error): Diese statische Methode wird aufgerufen, nachdem ein Fehler von einer untergeordneten Komponente geworfen wurde. Sie erhält den geworfenen Fehler als Argument und gibt einen Wert zurück, um den Zustand der Komponente zu aktualisieren.componentDidCatch(error, info): Diese Lebenszyklusmethode wird aufgerufen, nachdem ein Fehler von einer untergeordneten Komponente geworfen wurde. Sie erhält den geworfenen Fehler als Argument sowie ein Objekt mit Informationen darüber, welche Komponente den Fehler geworfen hat. Sie können sie zur Protokollierung von Fehlerinformationen verwenden.
Wichtig ist, dass Fehlergrenzen nur Fehler in der Rendering-Phase, in Lebenszyklusmethoden und in Konstruktoren des gesamten Baumes unter ihnen abfangen. Sie fangen Fehler nicht ab für:
- Event-Handler (mehr dazu im folgenden Abschnitt)
- Asynchronen Code (z.B.
setTimeout- oderrequestAnimationFrame-Callbacks) - Serverseitiges Rendering
- Fehler, die in der Fehlergrenze selbst (statt in ihren Kindern) geworfen werden
Fehlergrenzen und React Hooks: Eine leistungsstarke Kombination
Während traditionell Klassenkomponenten zur Implementierung von Fehlergrenzen verwendet wurden, bieten React Hooks einen prägnanteren und funktionaleren Ansatz. Wir können einen wiederverwendbaren useErrorBoundary-Hook erstellen, der die Fehlerbehandlungslogik kapselt und eine bequeme Möglichkeit bietet, Komponenten zu umschließen, die beim Laden von Ressourcen Fehler auslösen könnten.
Einen benutzerdefinierten useErrorBoundary-Hook erstellen
Hier ist ein Beispiel für einen useErrorBoundary-Hook:
import { useState, useCallback } from 'react';
function useErrorBoundary() {
const [error, setError] = useState(null);
const resetError = useCallback(() => {
setError(null);
}, []);
const captureError = useCallback((e) => {
setError(e);
}, []);
const ErrorBoundary = useCallback(({ children, fallback }) => {
if (error) {
return fallback ? fallback : An error occurred: {error.message || String(error)};
}
return children;
}, [error]);
return { ErrorBoundary, captureError, error, resetError };
}
export default useErrorBoundary;
Erklärung:
useState: Wir verwendenuseState, um den Fehlerzustand zu verwalten. Es setzt den Fehler anfangs aufnull.useCallback: Wir verwendenuseCallback, um die FunktionenresetErrorundcaptureErrorzu memoizieren. Dies ist eine gute Praxis, um unnötige Neu-Renderings zu verhindern, wenn diese Funktionen als Props weitergegeben werden.ErrorBoundaryKomponente: Dies ist eine mituseCallbackerstellte funktionale Komponente, diechildrenund eine optionalefallback-Prop entgegennimmt. Wenn ein Fehler im Zustand vorhanden ist, rendert sie entweder die bereitgestelltefallback-Komponente oder eine Standard-Fehlermeldung. Andernfalls rendert sie die Kinder. Dies fungiert als unsere Fehlergrenze. Das Abhängigkeitsarray `[error]` stellt sicher, dass sie bei einer Änderung des `error`-Zustands neu gerendert wird.captureErrorFunktion: Diese Funktion wird verwendet, um den Fehlerzustand zu setzen. Sie rufen diese innerhalb einestry...catch-Blocks beim Laden von Ressourcen auf.resetErrorFunktion: Diese Funktion löscht den Fehlerzustand, sodass die Komponente ihre Kinder neu rendern kann (und möglicherweise den Ladeversuch der Ressource wiederholt).
Implementierung des Ressourcenladens mit Fehlerbehandlung
Sehen wir uns nun an, wie dieser Hook verwendet wird, um Fehler beim Laden von Ressourcen zu behandeln. Betrachten wir eine Komponente, die Benutzerdaten von einer API abruft:
import React, { useState, useEffect } from 'react';
import useErrorBoundary from './useErrorBoundary';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const { ErrorBoundary, captureError, error, resetError } = useErrorBoundary();
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (e) {
captureError(e);
}
};
fetchData();
}, [userId, captureError]);
if (error) {
return (
Failed to load user data. {user.name}
Email: {user.email}
{/* Other user details */}Erklärung:
- Wir importieren den
useErrorBoundary-Hook. - Wir rufen den Hook auf, um die
ErrorBoundary-Komponente, diecaptureError-Funktion, denerror-Zustand und dieresetError-Funktion zu erhalten. - Innerhalb des
useEffect-Hooks umschließen wir den API-Aufruf mit einemtry...catch-Block. - Wenn während des API-Aufrufs ein Fehler auftritt, rufen wir
captureError(e)auf, um den Fehlerzustand zu setzen. - Wenn der
error-Zustand gesetzt ist, rendern wir dieErrorBoundary-Komponente. Wir stellen eine benutzerdefiniertefallback-Prop bereit, die eine Fehlermeldung und eine „Erneut versuchen“-Schaltfläche anzeigt. Ein Klick auf die Schaltfläche ruftresetErrorauf, um den Fehlerzustand zu löschen, was ein Neu-Rendern und einen weiteren Versuch zum Abrufen der Daten auslöst. - Wenn kein Fehler aufgetreten ist und die Benutzerdaten geladen sind, rendern wir die Details des Benutzerprofils.
Umgang mit verschiedenen Arten von Fehlern beim Laden von Ressourcen
Verschiedene Arten von Fehlern beim Laden von Ressourcen können unterschiedliche Behandlungsstrategien erfordern. Hier sind einige häufige Szenarien und wie man sie angeht:
Netzwerkfehler
Netzwerkfehler treten auf, wenn der Client keine Verbindung zum Server herstellen kann (z. B. aufgrund eines Netzwerkausfalls oder einer Server-Downtime). Das obige Beispiel behandelt bereits grundlegende Netzwerkfehler mit `response.ok`. Sie könnten eine ausgefeiltere Fehlererkennung hinzufügen, zum Beispiel:
//Inside the fetchData function
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
// Consider adding specific error code handling
if (response.status === 404) {
throw new Error("User not found");
} else if (response.status >= 500) {
throw new Error("Server error. Please try again later.");
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
}
const data = await response.json();
setUser(data);
} catch (error) {
if (error.message === 'Failed to fetch') {
// Likely a network error
captureError(new Error('Network error. Please check your internet connection.'));
} else {
captureError(error);
}
}
In diesem Fall können Sie dem Benutzer eine Nachricht anzeigen, die auf ein Problem mit der Netzwerkverbindung hinweist und vorschlägt, die Internetverbindung zu überprüfen.
API-Fehler
API-Fehler treten auf, wenn der Server eine Fehlerantwort zurückgibt (z. B. ein 400 Bad Request oder ein 500 Internal Server Error). Wie oben gezeigt, können Sie `response.status` überprüfen und diese Fehler entsprechend behandeln.
Fehler beim Parsen von Daten
Fehler beim Parsen von Daten treten auf, wenn die Antwort vom Server nicht im erwarteten Format vorliegt und nicht geparst werden kann (z. B. ungültiges JSON). Sie können diese Fehler behandeln, indem Sie den Aufruf von response.json() in einen try...catch-Block einschließen:
//Inside the fetchData function
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (error) {
if (error instanceof SyntaxError) {
captureError(new Error('Failed to parse data from server.'));
} else {
captureError(error);
}
}
Fehler beim Laden von Bildern
Für das Laden von Bildern können Sie den onError-Event-Handler am <img>-Tag verwenden:
function MyImage({ src, alt }) {
const { ErrorBoundary, captureError } = useErrorBoundary();
const [imageLoaded, setImageLoaded] = useState(false);
const handleImageLoad = () => {
setImageLoaded(true);
};
const handleImageError = (e) => {
captureError(new Error(`Failed to load image: ${src}`));
};
return (
Failed to load image.